﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;

namespace main.sdk.input
{
    public class KeyWrapper
    {
        /* Botões */
        public const int GamePadA        = 0x000;
        public const int GamePadB        = 0x001;
        public const int GamePadX        = 0x002;
        public const int GamePadY        = 0x003;
        public const int GamePadLS       = 0x004;
        public const int GamePadRS       = 0x005;
        public const int GamePadLB       = 0x006;
        public const int GamePadRB       = 0x007;
        public const int GamePadDown     = 0x008;
        public const int GamePadUp       = 0x009;
        public const int GamePadLeft     = 0x00A;
        public const int GamePadRight    = 0x00B;
        public const int GamePadBack     = 0x00C;
        public const int GamePadStart    = 0x00D;

       
        /* Eixos */
        public const int GamePadLT       = 0x101;
        public const int GamePadRT       = 0x102;
        public const int GamePadLSUp     = 0x103;
        public const int GamePadLSDown   = 0x104;
        public const int GamePadLSLeft   = 0x105;
        public const int GamePadLSRight  = 0x106;
        public const int GamePadRSDown   = 0x107;
        public const int GamePadRSUp     = 0x108;
        public const int GamePadRSLeft   = 0x109;
        public const int GamePadRSRight  = 0x10A;

        /**
         * Classe interna do iterador.
         */
        private class WrapIterator
        {
            public bool  Continue = true;
            public float Value    = 0;

            public void Down(float value)
            {
                Continue = false;
                Value = value;
            }
        }

        /**
         * Classe interna de função.
         */
        private class WrapFunction
        {
            public Func<WrapIterator, WrapFunction, WrapIterator> Function = null;
            public List<Object> Parameters = new List<Object>();

            public WrapIterator Call(WrapIterator It)
            {
                return Function(It, this);
            }
        }

        /* Variaveis Estáticas. */
        private static readonly Buttons[] _GamepadButtons = { Buttons.A, Buttons.B, Buttons.X, Buttons.Y, Buttons.LeftStick, Buttons.RightStick, Buttons.LeftShoulder, Buttons.RightShoulder, Buttons.DPadDown, Buttons.DPadUp, Buttons.DPadLeft, Buttons.DPadRight, Buttons.Back, Buttons.Start };
        
        /* Variaveis */
        private List<WrapFunction> _CheckFunctions = new List<WrapFunction>();

        /**
         * Dá wrap em uma tecla.
         */
        public KeyWrapper WrapKey(Keys key)
        {
            WrapFunction Function = new WrapFunction();
            Function.Parameters.Add(key);
            Function.Function = (iterator, function) =>
            {
                if (Input.Down((Keys)Function.Parameters[0]))
                    iterator.Down(1);
                return iterator;
            };
            _CheckFunctions.Add(Function);
            return this;
        }

        /**
         * Função privada para gerar o wrap de um botão do gamepad.
         */
        private void SetButtonFunction(WrapFunction Function, Buttons button)
        {
            Function.Parameters.Add(button);
            Function.Function = (iterator, function) =>
            {
                if (Gamepad.Get((int)Function.Parameters[0]).Down((Buttons)Function.Parameters[1]))
                    iterator.Down(1);
                return iterator;
            };
        }

        /**
         * Dá wrap em um botão do gamepad.
         */
        public KeyWrapper WrapGamepad(int Index, int GamePadKey)
        {
            WrapFunction Function = new WrapFunction();
            Function.Parameters.Add(Index);

            /* Verifica por eixos */
            if ((GamePadKey & 0x100) != 0)
            {
                switch (GamePadKey)
                {
                    case GamePadLT:
                        {
                            Function.Function = (iterator, function) =>
                            {
                                float Value = Gamepad.Get((int)Function.Parameters[0]).LT;
                                if (Value >= 0.01)
                                    iterator.Down(Value);
                                return iterator;
                            };
                            break;
                        }
                    case GamePadRT:
                        {
                            Function.Function = (iterator, function) =>
                            {
                                float Value = Gamepad.Get((int)Function.Parameters[0]).RT;
                                if (Value >= 0.01)
                                    iterator.Down(Value);
                                return iterator;
                            };
                            break;
                        }
                    case GamePadLSUp:
                        {
                            Function.Function = (iterator, function) =>
                            {
                                float Value = Gamepad.Get((int)Function.Parameters[0]).LSY;
                                if (Value >= 0.01)
                                    iterator.Down(Value);
                                return iterator;
                            };
                            break;
                        }
                    case GamePadLSDown:
                        {
                            Function.Function = (iterator, function) =>
                            {
                                float Value = Gamepad.Get((int)Function.Parameters[0]).LSY;
                                if (Value <= -0.01)
                                    iterator.Down(-Value);
                                return iterator;
                            };
                            break;
                        }
                    case GamePadLSRight:
                        {
                            Function.Function = (iterator, function) =>
                            {
                                float Value = Gamepad.Get((int)Function.Parameters[0]).LSX;
                                if (Value >= 0.01)
                                    iterator.Down(Value);
                                return iterator;
                            };
                            break;
                        }
                    case GamePadLSLeft:
                        {
                            Function.Function = (iterator, function) =>
                            {
                                float Value = Gamepad.Get((int)Function.Parameters[0]).LSX;
                                if (Value <= -0.01)
                                    iterator.Down(-Value);
                                return iterator;
                            };
                            break;
                        }
                    case GamePadRSUp:
                        {
                            Function.Function = (iterator, function) =>
                            {
                                float Value = Gamepad.Get((int)Function.Parameters[0]).LSY;
                                if (Value >= 0.01)
                                    iterator.Down(Value);
                                return iterator;
                            };
                            break;
                        }
                    case GamePadRSDown:
                        {
                            Function.Function = (iterator, function) =>
                            {
                                float Value = Gamepad.Get((int)Function.Parameters[0]).LSY;
                                if (Value <= -0.01)
                                    iterator.Down(-Value);
                                return iterator;
                            };
                            break;
                        }
                    case GamePadRSRight:
                        {
                            Function.Function = (iterator, function) =>
                            {
                                float Value = Gamepad.Get((int)Function.Parameters[0]).RSX;
                                if (Value >= 0.01)
                                    iterator.Down(Value);
                                return iterator;
                            };
                            break;
                        }
                    case GamePadRSLeft:
                        {
                            Function.Function = (iterator, function) =>
                            {
                                float Value = Gamepad.Get((int)Function.Parameters[0]).RSX;
                                if (Value <= -0.01)
                                    iterator.Down(-Value);
                                return iterator;
                            };
                            break;
                        }
                }
            }
            /* Botões normais. */
            else
            {
                SetButtonFunction(Function, _GamepadButtons[GamePadKey]);
            }
            _CheckFunctions.Add(Function);
            return this;
        }

        /**
         * Valor do iterador.
         */
        public float Value()
        {
            WrapIterator It = new WrapIterator();
            foreach (WrapFunction CheckFunc in _CheckFunctions)
            {
                It = CheckFunc.Call(It);
                if (!It.Continue)
                    break;
            }
            return It.Value;
        }

        /**
         * A tecla está pressionada.
         */
        public bool Down()
        {
            WrapIterator It = new WrapIterator();
            foreach (WrapFunction CheckFunc in _CheckFunctions)
            {
                It = CheckFunc.Call(It);
                if (!It.Continue)
                    break;
            }
            return (Math.Abs(It.Value) > 0.05f);
        }
    }
}
